package com.webflux.handler;

import com.webflux.config.WebSocketMapping;
import com.webflux.config.WebSocketWrap;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.socket.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * 微信公众号消息通知websocket处理服务
 *
 * @author 晨曦
 */
@Component
@Slf4j
@WebSocketMapping("/each")
public class WeChatNoticeHandle implements WebSocketHandler {
    final String HandlerCode = "X_Access_Token";

    @SneakyThrows
    @Override
    public Mono<Void> handle(WebSocketSession session) {
        HandshakeInfo handshakeInfo = session.getHandshakeInfo();
        //解析URL上的所有参数
        Map<String, String> queryMap = getQueryMap(handshakeInfo.getUri().getQuery());
        //当前用户登录Token
        String token;
        //解析读取请求体上的token信息
        // String query = session.getHandshakeInfo().getUri().getQuery();
        String id = queryMap.get("id");
        log.info("输出id->{}", id);
//        if (StringUtils.hasText(query) && query.contains(HandlerCode)) {
//            token = queryMap.get(HandlerCode);
//        } else if (session.getHandshakeInfo().getHeaders().containsKey(HandlerCode)) {
//            token = session.getHandshakeInfo().getHeaders().getFirst(HandlerCode);
//        } else {
//            String paths = session.getHandshakeInfo().getUri().getPath();
//            String[] path = paths.split("[/]");
//            if (path.length == 0) {
//                return Mono.empty();
//            }
//            token = path[path.length - 1];
//        }
        if (!ObjectUtils.isEmpty(id)) {
            // 暂时只校验了是否携带了ID,以后可以改为校验TOKEN
            // 输入输出封装
            Mono<Void> input = session.receive().doOnNext(message -> this.messageHandle(session, message))
                    .log()
                    .doOnError(throwable -> log.error("webSocket发生异常：" + throwable))
                    .doOnComplete(() -> log.info("webSocket结束")).then();
//            Mono<Void> output = session.send(Flux.create(sink ->
//                    WebSocketWrap.SENDER.put(id, new WebSocketWrap(id, session, sink))));
            Mono<Void> output = session.send(Flux.create(sink -> WebSocketWrap.SENDER
                    .put(id, new WebSocketWrap(id, session, sink.next(session.textMessage("测试消息"))))));
           // TimeUnit.SECONDS.sleep(2);
            this.sendLoginMsg(id);
            return Mono.zip(input, output).then();
        } else {
            return session.close(new CloseStatus(1016, "连接未通过校验,即将关闭连接"));
        }

//        //根据用户token获取用户信息
//        return userTokenMap
//                //.getByToken(token)
//                .switchIfEmpty(Mono.defer(() -> {
//                    //客户端发送给服务端的消息处理
//                    Mono<Void> inputServer = session
//                            .receive()
//                            .map(WebSocketMessage::getPayloadAsText)
//                            .map(message -> {
//                                log.info("【非平台连接】websocket连接服务，收到来自客户端的消息：{}", message);
//                                return message;
//                            })
//                            .then();
//
//                    //服务端给客户端推送消息
//                    Mono<Void> outputClient = session.send(Flux.create(sink -> senderMap
//                                    .put(queryMap.getOrDefault("userId", "defaultId"), new WebSocketSender(session, sink))));
//
//                    return Mono.zip(inputServer, outputClient).then(Mono.empty());
//                }))
////                .map(UserToken::getUserId)
//                .flatMap(userId -> {
//                    //客户端发送给服务端的消息处理
////                    Mono<Void> inputServer = session.receive()
////                            .map(WebSocketMessage::getPayloadAsText)
////                            .map(message -> { log.info("【微信公众号】websocket连接服务，收到来自客户端用户【{}】的消息：{}", userId, message);
////                                return message;
////                            }).then();
//                    Mono<Void> inputServer = session.receive().doOnNext(message -> this.messageHandle(session, message))
//                    .log()
//                    .doOnError(throwable -> log.error("webSocket发生异常：" + throwable))
//                    .doOnComplete(() -> log.info("webSocket结束")).then();
//                    //服务端给客户端推送消息
//                    Mono<Void> outputClient = session.send(Flux.create(sink -> senderMap.put(token, new WebSocketSender(session, sink))));
//                    return Mono.zip(inputServer, outputClient).then();
//                });
    }

    /**
     * 接收客户端请求的处理回调
     *
     * @param session
     * @param message
     */
    private void messageHandle(WebSocketSession session, WebSocketMessage message) {
        // 接收客户端请求的处理回调
        session.getId();
        switch (message.getType()) {
            case TEXT:
                try {
                    String text = message.getPayloadAsText();
                    log.info("Session ->{},输出Message信息：->{}",session.getId(), text);
                    // todo 业务处理
                    break;
                } catch (Exception e) {
                    log.warn("无法处理的消息", e);
                }
            case BINARY:
            case PONG:
            case PING:
                break;
            default:
        }
    }

    /**
     * 通知所有连接登录信息
     * @param id
     */
    private void sendLoginMsg(String id){
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        WebSocketWrap.broadcastText("系统时间->"+LocalDateTime.now().format(fmt)+",用户"+id+"登录系统。");
    }

    /**
     * 转换query参数 String转Map
     *
     * @param queryStr
     * @return
     */
    private Map<String, String> getQueryMap(String queryStr) {
        Map<String, String> queryMap = new HashMap<>(4);
        if (!StringUtils.isEmpty(queryStr)) {
            String[] queryParam = queryStr.split("&");
            Arrays.stream(queryParam).forEach(s -> {
                String[] kv = s.split("=", 2);
                String value = kv.length == 2 ? kv[1] : "";
                queryMap.put(kv[0], value);
            });
        }
        return queryMap;
    }
}
